use serde::{Deserialize, Serialize};
use axum::response::{Html, IntoResponse, Redirect, Response};
use validator::Validate;
use std::{collections::HashMap, fmt::Debug};
use axum::{Extension, http, Json, TypedHeader};
use jsonwebtoken::{encode, EncodingKey, Header};
use jsonwebtoken::errors::Error;
use serde_json::json;
use config::CONFIG;

use cores::responses::CusResponse;
use cores::validator::{ValidatedForm, ValidatedJson, ValidatedQuery};
use cores::{AppError, Result};
use cores::secret::pbkdf2_secret_verify;
use crate::service::{fetch_application_by_id, fetch_user_by_id, fetch_user_by_username, get_auth_applications_by_user, grant_application};
use crate::templates::{App, AppContentTemplate, OauthAppsTemplate, OauthLoginTemplate};
use askama::Template;
use axum::headers::{Cookie, HeaderMap, HeaderValue};
use cores::sessions::{Session, SessionData};
use crate::dao::User;
use crate::middleware::UserAuthMiddleware;

#[derive(Debug, Deserialize, Serialize)]
enum ResponseTypeEnum {
    #[serde(rename = "code")]
    Code,
    #[serde(rename = "login")]
    Login,
}

#[derive(Debug, Deserialize, Validate, Serialize)]
pub struct Auth2Form {
    response_type: ResponseTypeEnum,
    client_id: Option<String>,
    state: Option<String>,
    redirect_uri: Option<String>,
    scope: Option<String>,
}

#[derive(Debug, Deserialize, Validate, Serialize)]
pub struct LoginForm {
    username: Option<String>,
    password: Option<String>,
}

pub async fn logout(
    Extension(session): Extension<Session>,
    TypedHeader(cookie): TypedHeader<Cookie>,
) -> Result<Redirect> {
    session.clear(cookie).await?;
    Ok(Redirect::to("/api/oauth2/authorize?response_type=login"))
}

pub async fn auth2_authorize(
    ValidatedQuery(auth2_query): ValidatedQuery<Auth2Form>,
) -> Result<Html<String>> {
    match auth2_query.response_type {
        ResponseTypeEnum::Code => {
            let application = fetch_application_by_id(auth2_query.client_id.unwrap()).await?;
            let html = OauthLoginTemplate {
                name: application.client_name.unwrap(),
                icon: application.client_icon.unwrap(),
            }.render()?;
            Ok(Html(html))
        }
        ResponseTypeEnum::Login => {
            let html = OauthLoginTemplate {
                name: "Smart7 Apps Platform".to_string(),
                icon: "https://cdn.smart7.tech/static/img/Perfect7.png".to_string(),
            }.render()?;
            Ok(Html(html))
        }
    }
}


pub async fn get_my_auth_application_list(
    UserAuthMiddleware(user): UserAuthMiddleware,
) -> Result<Html<String>> {
    let apps = get_auth_applications_by_user(user).await;
    let apps: Vec<App> = apps.into_iter().map(move |app| App {
        redirect_uri: app.redirect_url.unwrap(),
        client_icon: app.client_icon.unwrap(),
        client_name: app.client_name.unwrap(),
    }).collect();
    let html = OauthAppsTemplate {
        apps,
    }.render()?;
    Ok(Html(html))
}

async fn auth2_login_handler(user: User, auth2_query: Auth2Form, session: Session) -> Result<impl IntoResponse> {
    match auth2_query.response_type {
        ResponseTypeEnum::Code => {
            let application = fetch_application_by_id(auth2_query.client_id.unwrap().clone()).await?;
            grant_application(user.clone(), application).await?;
            let redirect_uri = format!(
                "{url}?code={code}&state={state}",
                url = auth2_query.redirect_uri.unwrap(),
                code = user.id.unwrap(),
                state = auth2_query.state.unwrap()
            );
            // 创建请求头
            let headers = HeaderMap::new();
            Ok((headers, CusResponse::ok(redirect_uri)))
        }
        ResponseTypeEnum::Login => {
            // 创建请求头
            let mut headers = HeaderMap::new();
            // 创建缓存数据
            let mut session_data = SessionData::new();
            session_data.insert(
                "user_id".to_string(),
                user.id.unwrap().to_string(),
            );
            // 存储session并获取cookie键名键值
            let cookie_value = session.set_session_data(session_data).await;
            // 将cookie放入请求头
            headers.insert(
                http::header::SET_COOKIE,
                HeaderValue::from_str(&cookie_value).unwrap(),
            );
            Ok((headers, CusResponse::ok("/api/oauth2/get_my_auth_application_list".to_string())))
        }
    }
}

pub async fn auth2_login(
    ValidatedQuery(auth2_query): ValidatedQuery<Auth2Form>,
    ValidatedForm(login_form): ValidatedForm<LoginForm>,
    TypedHeader(cookie): TypedHeader<Cookie>,
    Extension(session): Extension<Session>,
) -> Result<impl IntoResponse> {
    match session.get_session_data(cookie).await {
        Ok(session_data) => {
            let user_id = session_data.get("user_id")?;
            let user = fetch_user_by_id(user_id).await?;
            auth2_login_handler(user, auth2_query, session).await
        }
        Err(_) => {
            let user = fetch_user_by_username(login_form.username.unwrap()).await?;
            if pbkdf2_secret_verify(
                login_form.password.unwrap(),
                user.password.clone().unwrap(),
                CONFIG.read().await.secret().to_string(),
            ).is_err() {
                return Err(AppError::from_str("密码错误", 403));
            }
            auth2_login_handler(user, auth2_query, session).await
        }
    }
}


#[derive(Debug, Deserialize, Validate, Serialize)]
pub struct TokenForm {
    grant_type: String,
    code: String,
    redirect_uri: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct AuthPayload {
    id: String,
    avatar: String,
    username: String,
    discriminator: String,
}

impl IntoResponse for AuthPayload {
    fn into_response(self) -> Response {
        Json(self).into_response()
    }
}


#[derive(Debug, Serialize)]
pub struct Token {
    access_token: String,
    token_type: String,
}

pub async fn auth2_token(
    ValidatedForm(token_form): ValidatedForm<TokenForm>
) -> Result<Json<Token>> {
    let user = fetch_user_by_id(token_form.code.clone()).await?;
    println!("{:#?}", token_form);
    let claims = AuthPayload {
        id: user.id.unwrap().to_string(),
        avatar: "2".to_string(),
        username: user.username.unwrap(),
        discriminator: "4".to_string(),
    };
    let access_token = encode(
        &Header::default(),
        &claims,
        &EncodingKey::from_secret("t7nviGR9QXxk&gAie9Uw~%rjXBzd#%M$".as_ref()),
    )?;
    Ok(Json(Token { access_token, token_type: "Bearer".to_string() }))
}


#[derive(Debug, Serialize, Validate, Deserialize)]
pub struct AppContentQuery {
    pub application_url: String,
}

pub async fn app_content(
    ValidatedQuery(app_content_query): ValidatedQuery<AppContentQuery>
) -> Result<Html<String>> {
    let html = AppContentTemplate {
        application_url: app_content_query.application_url,
    }.render()?;
    Ok(Html(html))
}

pub async fn myinfo() -> Result<AuthPayload> {
    println!("get myinfo");
    Ok(AuthPayload {
        id: "1".to_string(),
        avatar: "2".to_string(),
        username: "3".to_string(),
        discriminator: "4".to_string(),
    })
}